using System;
using System.Collections.Generic;
using FireStarter.Base;
using FireStarter.CodeGeneration;
using FireStarter.CodeGeneration.Generator;
using FireStarter.CodeGeneration.Parser;
using FireStarter.Test.CodeGeneration;
using FireStarterModeller.BO;
using Habanero.BO;
using NUnit.Framework;
 

namespace FireStarter.Test.CodeGeneration.Templates
{
    //TODO: shouldn't need to set parent property on modelelements (foreach should figure it out)

    [TestFixture]
    public class TestTestUtils : TestBase
    {
        private const string TEST_UTILS_HEADER = @"using System;
using System.Collections.Generic;
using TestProject.BO;
using Habanero.Base;
using Habanero.BO;
using Habanero.BO.ClassDefinition;
using Habanero.DB;
using Habanero.Util;

namespace TestProject.Test.BO
{
    /// <summary>
    /// Provides utilities for testing the BusinessObjects in this application.
    /// -------------------------------------------------------------------------------
    /// Use this class to provide overrides when the auto-generated Create methods
    /// in TestUtilsBase are not suitable for the BusinessObjects, usually due to
    /// custom rules or restricted data types.  The generated tests will call these
    /// overrides instead.
    /// -------------------------------------------------------------------------------
    /// </summary>
    public class TestUtilsCodeGen : TestUtilsBase
    {
        // Example of overriding a Create method
        //public new static Car CreateUnsavedValidCar()
        //{
        //    Car car = TestUtilsBase.CreateUnsavedValidCar();
        //    car.Model = ""Toyota"";
        //    return car;
        //}
    }
    
    /// <summary>
    /// Provides a set of utilities to be used by tests for BusinessObjects in this
    /// application.  The default utilities to create sample BusinessObjects are located
    /// at the top of the Def file for each BO test.
    ///
    /// You can safely add additional utilities to this particular file since it is
    /// only written once, whereas the Def classes are overwritten with each generation.
    /// </summary>
    public partial class TestUtilsBase
    {
        private static Random _randomGenerator;

        protected static Random Random
        {
            get
            {
                if (_randomGenerator == null) _randomGenerator = new Random();
                return _randomGenerator;
            }
        }

        public static int GetRandomInt()
        {
             return GetRandomInt(100000); 
        }
        
        public static int GetRandomInt(int max)
        {
             return Random.Next(0, max); 
        }

		public static int GetRandomInt(int min,int max)
        {
            return Random.Next(min, max); 
        }

        public static string GetRandomString()
        {
             return GetRandomInt().ToString(); 
        }
        
        public static string GetRandomString(int maxLength)
        {            
             if (maxLength > 9) maxLength = 9;
             int max = Convert.ToInt32(Math.Pow(10, maxLength)) - 1;
             return GetRandomInt(max).ToString();          
        }
        
        public static string GetRandomString(int minLength, int maxLength)
        {
             if (minLength > 9) minLength = 9;
             int min = Convert.ToInt32(Math.Pow(10, minLength)) - 1;
             if (maxLength > 9) maxLength = 9;
             int max = Convert.ToInt32(Math.Pow(10, maxLength)) - 1;
             return GetRandomInt(min,max).ToString();
        }
        
        public static bool GetRandomBoolean()
        {
             return (GetRandomInt(100000) > 50000); 
        }
        
        public static DateTime GetRandomDate()
        {
            return DateTime.Now;
        }
        
        public static DateTime GetRandomDate(string max)
        {
            string start = DateTime.MinValue.ToString(""yyyy/MM/dd"");
            return GetRandomDate(start,max);
        }
        
        public static DateTime GetRandomDate(string min, string max)
        {
            DateTime start = DateTime.Parse(min);
            
            int range = (DateTime.Parse(max) - start).Days;
            return start.AddDays(TestUtilsCodeGen.GetRandomInt(range));
        }
        
        /// <summary>
        /// Takes a lookup list generated by Habanero and randomly selects a value
        /// from the list
        /// </summary>
        public static object GetRandomLookupListValue(Dictionary<string, object> lookupList)
        {
            object[] values = new object[lookupList.Count];
            lookupList.Values.CopyTo(values, 0);
            return values[TestUtilsCodeGen.GetRandomInt(0, values.Length - 1)];
        }
        
        /// <summary>
        /// Waits for the garbage collector to clear dereferenced objects in order
        /// to ensure accurate testing
        /// </summary>
        public static void WaitForGC()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }        
    }
}";

        [Ignore("TODO: fix the template names")]
        [Test]
        public void Test_NoBusinessObjects()
        {
            //---------------Set up test pack-------------------
            string expectedTemplate = TEST_UTILS_HEADER;

            string template = FireStarter.CodeGeneration.Templates.Test_BO_TestUtils;
            IList<IModelClass> modelClasses = new List<IModelClass>();
            StatementParser statementParser = new StatementParser();
            DMSolution modelProject = new DMSolution();
            modelProject.BOProjectName = "TestProject.BO";
            modelProject.BOTestProjectName = "TestProject.Test.BO";
            DMAssembly modelAssembly = new DMAssembly(modelProject.BOProjectName);
            modelProject.Assemblies.Add(modelAssembly);
            foreach (IModelClass modelClass in modelClasses)
            {
                modelAssembly.Classes.Add((DMClass)modelClass);
            }
            
            ModelStrategyMapper modelStrategyMapper = GetModelStrategyMapper();
            //-------------Assert Preconditions -------------

            //---------------Execute Test ----------------------
            TemplateExpression templateExpression = statementParser.Parse(ref template);
            string generatedTemplate = templateExpression.Generate(modelStrategyMapper, modelProject);
            //---------------Test Result -----------------------
            StripSpaceAndNewLines(ref expectedTemplate, ref generatedTemplate);
            Assert.AreEqual(expectedTemplate, generatedTemplate);
        }

        private ModelStrategyMapper GetModelStrategyMapper()
        {
            ModelStrategyMapper modelStrategyMapper = new ModelStrategyMapper();
            ReflectionGenerationStrategy strategy = new ReflectionGenerationStrategy();
            modelStrategyMapper.SetStrategy(typeof(IModelElement), strategy);
            return modelStrategyMapper;
        }

        [Test]
        public void TestGetRandomDate()  //Copies code from the template to check that it works
        {
            //---------------Set up test pack-------------------
            string minDate = "2008/11/10";
            string maxDate = "2008/11/21";
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            string randomDate = RandomDate(minDate, maxDate).ToString("yyyy/MM/dd");
            //---------------Test Result -----------------------
            
            Assert.GreaterOrEqual(randomDate,minDate);
            Assert.LessOrEqual(randomDate,maxDate);

        }


        private DateTime RandomDate(string min, string max)
        {
            DateTime start = DateTime.Parse(min);
            
            int range = (DateTime.Parse(max) - start).Days;
            return start.AddDays(TestUtilsCodeGen.GetRandomInt(range));
        }

        private static void StripSpaceAndNewLines(ref string expectedTemplate, ref string generatedTemplate)
        {
            expectedTemplate = expectedTemplate.Replace(" ", "");
            expectedTemplate = expectedTemplate.Replace("\r\n", "");
            expectedTemplate = expectedTemplate.Replace("\t", "");
            generatedTemplate = generatedTemplate.Replace("\t", "");
            generatedTemplate = generatedTemplate.Replace("\r\n", "");
            generatedTemplate = generatedTemplate.Replace(" ", "");
        }
    }
}